package city.spring.shift;

import com.google.common.collect.HashBasedTable;
import com.google.common.collect.Table;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.*;

/**
 * 排班方案
 *
 * @author HouKunLin
 * @date 2020/5/28 0028 22:51
 */
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Plan {
    private static final Logger logger = LoggerFactory.getLogger(Plan.class);
    /**
     * 方案名称
     */
    private String title;
    /**
     * 排班天数
     */
    private Integer days;
    /**
     * 每天班次信息
     */
    private List<Period> periods;
    /**
     * 该方案涉及的人员列表信息
     */
    private List<People> peoples;


    /**
     * 计算排班
     */
    public Table<Integer, String, List<People>> calc() {
        Table<Integer, String, List<People>> table = HashBasedTable.create();

        Integer oneDayNeedPeoples = periods.stream().map(Period::getNumber).reduce(Integer::sum).get();
        if (peoples.size() < oneDayNeedPeoples) {
            throw new RuntimeException("当前排班方案一天至少需要 " + oneDayNeedPeoples + " 人，但是目前只有 " + peoples.size() + " 人");
        }

        for (int day = 0; day < days; day++) {
            // 循环排班天数
            for (Period period : periods) {
                // 循环排班班次
                String colKey = period.getTitle();
                table.put(day, colKey, new ArrayList<>());
                fillPeople(period, table, day, colKey);
            }
        }
        showTable(table);
        return table;
    }

    public void showTable(Table<Integer, String, List<People>> table) {
        System.out.println();
        System.out.println();
        System.out.println(table);
        for (People people : peoples) {
            System.out.println(people);
        }
        for (Map.Entry<Integer, Map<String, List<People>>> entry : table.rowMap().entrySet()) {
            logger.info("第 {} 天", entry.getKey());
            entry.getValue().forEach((s, strings) -> {
                // logger.info("第 {} 天，班次：{}，人员：{}", entry.getKey(), s, strings);
                logger.info("       {}  {}", s, strings);
            });
        }
    }

    /**
     * 填充人员信息
     *
     * @param period 当前安排的班次
     * @param table  存放所有数据的表格
     * @param rowKey 当前所处行
     * @param colKey 当前所处列
     */
    public void fillPeople(Period period, Table<Integer, String, List<People>> table, Integer rowKey, String colKey) {
        Integer number = period.getNumber();
        if (number > peoples.size()) {
            showTable(table);
            throw new RuntimeException("排班失败，" + period.getTitle() + " 需要 " + number + " 人，但是人员总数只有 " + peoples
                    .size() + " 人");
        }
        int retryNumber = 0;
        List<People> values = table.get(rowKey, colKey);
        for (int peopleIndex = 0; peopleIndex < number; peopleIndex++) {
            People people = getPeople();
            boolean nowPeriodHasPeople = nowPeriodHasPeople(table, rowKey, colKey, people);
            boolean nowDayHasPeople = nowDayHasPeople(table, rowKey, colKey, people);
            if (nowPeriodHasPeople || nowDayHasPeople) {
                // 如果当前班次中已经存在了这个人员信息，则重新获取人员信息
                peopleIndex--;
                retryNumber++;
                if (retryNumber > 1000) {
                    showTable(table);
                    throw new RuntimeException("排班失败，在第" + rowKey + "天的" + colKey + "班次重复获取人员超过1000次无法完成");
                }
                continue;
            }
            people.incUseNum();
            values.add(people);
        }
    }

    /**
     * 判断当前班次是否存在该人
     *
     * @param table  存放所有数据的表格
     * @param rowKey 当前所处行
     * @param colKey 当前所处列
     * @param people 当前人员
     * @return
     */
    public boolean nowPeriodHasPeople(Table<Integer, String, List<People>> table, Integer rowKey, String colKey, People people) {
        List<People> values = table.get(rowKey, colKey);
        return values.contains(people);
    }

    /**
     * 判断当前班次是否存在该人
     *
     * @param table  存放所有数据的表格
     * @param rowKey 当前所处行
     * @param colKey 当前所处列
     * @param people 当前人员
     * @return
     */
    public boolean nowDayHasPeople(Table<Integer, String, List<People>> table, Integer rowKey, String colKey, People people) {
        for (Map.Entry<Integer, Map<String, List<People>>> rowEntry : table.rowMap().entrySet()) {
            Integer row = rowEntry.getKey();
            if (!row.equals(rowKey)) {
                continue;
            }
            // 判断当天的班次是否存在该人
            Map<String, List<People>> colMap = rowEntry.getValue();
            for (Map.Entry<String, List<People>> colEntry : colMap.entrySet()) {
                List<People> peoples = colEntry.getValue();
                if (peoples.contains(people)) {
                    return true;
                }
            }
        }
        return false;
    }

    /**
     * 随机获取一个人员信息
     */
    public People getPeople() {
        // 人员乱序
        Collections.shuffle(peoples);
        // 对使用次数进行排序
        peoples.sort(Comparator.comparingInt(People::getUseNum));
        // System.out.println(peoples);
        return peoples.get(0);
        // int max = peoples.size();
        // int index = (int) Math.floor(0 + Math.random() * max);
        // return peoples.get(index);
    }
}
